// NOWHINE NC009 <- Shared file with system team so uses non-CamX file naming
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  iqsettingutil.cpp
/// @brief Util functions for IQ Setting Classes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "demux_1_3_0.h"
#include "iqcommondefs.h"
#include "iqsettingutil.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IQSettingUtils::SetupInterpolationTree
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL IQSettingUtils::SetupInterpolationTree(
    TuningNode*    pTipNode,
    UINT           numOfLevel,
    NodeOperation* pOperationTable,
    VOID*          pTriggerList)
{
    BOOL  result                 = TRUE;
    UINT  nodeIndexPerChildLevel = 0;    ///< Index of node at child level
    UINT  nodeIndex              = 0;    ///< Index of the node in the node array
    UINT  count1                 = 0;
    UINT  count2                 = 0;
    UINT  totalNodePerLevel      = 1;
    UINT  childNodeIndex         = 0;
    UINT  numChildNode           = 0;
    UINT  childCount             = 0;

    if ((NULL != pTipNode) && (0 != numOfLevel) && (NULL != pOperationTable))
    {
        nodeIndex = 0;

        // Go though each level above leaf node
        for (count1 = 0; count1 < (numOfLevel-1); count1++)
        {
            nodeIndexPerChildLevel = 0;

            // Calculate the number of nodes at this level
            if ( count1 > 0)
            {
                totalNodePerLevel = totalNodePerLevel * pOperationTable[count1 -1].numChildPerNode;
            }
            else
            {
                totalNodePerLevel = 1;
            }

            // Go through all the nodes at this level
            for (count2 = 0; count2 < totalNodePerLevel; count2++)
            {
                numChildNode = pOperationTable[count1].numChildPerNode;

                if (TRUE == pTipNode[nodeIndex + count2].isValid)
                {
                    // calculate the child node index
                    childNodeIndex = nodeIndex + totalNodePerLevel + nodeIndexPerChildLevel;
                    // search and set up the  child nodes
                    childCount = pOperationTable[count1].pSearchChildNode(
                                     &pTipNode[nodeIndex + count2], pTriggerList,
                                     &pTipNode[childNodeIndex]);

                    if (0 == childCount)
                    {
                        // Adding Fatal Error Logging
                        break;
                    }
                }

                nodeIndexPerChildLevel += numChildNode;
            }

            // Increate the overall cound index
            nodeIndex += totalNodePerLevel;
        }
    }

    return result;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IQSettingUtils::GainCurveSampling
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL IQSettingUtils::GainCurveSampling(
    FLOAT*       pKneeInput,
    FLOAT*       pKneeOutput,
    FLOAT*       pCoef,
    const FLOAT* pInput,
    FLOAT*       pOutput,
    UINT32       lutSize,
    FLOAT        rate,
    FLOAT        gain,
    UINT32       qFactor)
{
    FLOAT  xInputNorm  = 0.0f;
    FLOAT  yOutputNorm = 0.0f;
    UINT32 i;

    for (i = 1; i < (lutSize + 1); i++)
    {
        xInputNorm = pInput[i] / pInput[lutSize];
        yOutputNorm = 0;

        if (xInputNorm <= pKneeInput[1])
        {
            // actively boost dark area
            yOutputNorm = xInputNorm * gain;
        }
        else if (xInputNorm <= pKneeInput[1])
        {
            // fully recovered from under-exposure image
            yOutputNorm = pKneeOutput[1] + (xInputNorm - pKneeInput[1]) * (pCoef[0] + (xInputNorm - pKneeInput[1]) *
                (pCoef[1] + (xInputNorm - pKneeInput[1]) * pCoef[2]));
        }
        else
        {
            yOutputNorm = pKneeOutput[2] + (xInputNorm - pKneeInput[2]) * (pCoef[3] + (xInputNorm - pKneeInput[2]) *
                (pCoef[4] + (xInputNorm - pKneeInput[2]) * pCoef[5]));
        }

        yOutputNorm = IQSettingUtils::ClampFLOAT(yOutputNorm, MIN_NORM_OUTPUT_GAIN, MAX_NORM_OUTPUT_GAIN);

        pOutput[i] = yOutputNorm / xInputNorm;
        pOutput[i] = IQSettingUtils::ClampFLOAT(pOutput[i], MIN_NORM_OUTPUT_GAIN, gain);

    }
    pOutput[0] = pOutput[1];

    for (i = 0; i < (lutSize + 1); i++)
    {
        pOutput[i] = powf(pOutput[i], rate) *  static_cast<FLOAT>(1 << qFactor);
    }

    return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IQSettingUtils::InitializeNode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IQSettingUtils::InitializeNode(
    TuningNode* pNode,
    VOID*       pDataBuffer)
{
    pNode->level     = 0;
    pNode->isValid   = FALSE;
    pNode->numChild  = 0;
    pNode->pNodeData = NULL;
    pNode->pData     = pDataBuffer;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IQSettingUtils::AddNodeToInterpolationTree
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IQSettingUtils::AddNodeToInterpolationTree(
    TuningNode* pParentNode,
    TuningNode* pChildNode,
    VOID*       pData,
    VOID*       pTuningData)
{
    pChildNode->isValid   = TRUE;
    pChildNode->pNodeData = pData;

    if (NULL != pTuningData)
    {
        pChildNode->pData = pTuningData;
    }

    if (NULL != pParentNode)
    {
        pParentNode->pChildNode[pParentNode->numChild] = pChildNode;
        pParentNode->numChild++;
        pChildNode->level = pParentNode->level + 1;
    }
    else
    {
        // Starting with level 1
        pChildNode->level = 1;
    }
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IQSettingUtils::InterpolateTuningData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL IQSettingUtils::InterpolateTuningData(
    TuningNode*       pNode,
    UINT              numOfNoLeafNode,
    UINT              numOfLevel,
    DoInterpolation   pDoInterpolation)
{
    BOOL result = TRUE;

    if (NULL != pNode)
    {
        for (INT count = (numOfNoLeafNode - 1); count >= 0; count--)
        {
            if ((TRUE == pNode[count].isValid) &&
                (numOfLevel > pNode[count].level))
            {
                if (1 == pNode[count].numChild)
                {
                    if (count > 0)
                    {
                        pNode[count].pData = pNode[count].pChildNode[0]->pData;
                    }
                    else
                    {
                        // On the top node, we don't want to reuse the chromatix buffer
                        pDoInterpolation(
                            pNode[count].pChildNode[0]->pData,
                            pNode[count].pChildNode[0]->pData,
                            0,
                            pNode[count].pData);
                    }
                }
                else if (2 == pNode[count].numChild)
                {
                    pDoInterpolation(
                                     pNode[count].pChildNode[0]->pData,
                                     pNode[count].pChildNode[1]->pData,
                                     pNode[count].interpolationValue[0],
                                     pNode[count].pData);
                }
                else if (3 == pNode[count].numChild)
                {
                    pDoInterpolation(
                                     pNode[count].pChildNode[1]->pData,
                                     pNode[count].pChildNode[2]->pData,
                                     pNode[count].interpolationValue[1],
                                     pNode[count].pData);

                    pDoInterpolation(
                                     pNode[count].pChildNode[0]->pData,
                                     pNode[count].pData,
                                     pNode[count].interpolationValue[0],
                                     pNode[count].pData);
                }
            }
        }
    }
    else
    {
        /// @todo (CAMX-1812) Need to add logging for Common library
        result = FALSE;
    }
    return result;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IQSettingUtils::GetIndexPtTrigger
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IQSettingUtils::GetIndexPtTrigger(
    TriggerRegion*       pTriggerSet,
    UINT32               numRegion,
    FLOAT                triggerValue,
    InterpolationOutput* pOutput0)
{
    UINT32 startRegionIndex    = 0;
    FLOAT  interpolationRatio  = 0.0f;
    BOOL   regionIdentified    = FALSE;

    if ((numRegion > 0) && (NULL != pTriggerSet) && (NULL != pOutput0))
    {
        pOutput0->startIndex = 0;
        pOutput0->endIndex   = 0;

        for (startRegionIndex= 0; startRegionIndex < numRegion; startRegionIndex++)
        {
            // If trigger is beyone the last region
            if ((startRegionIndex == (numRegion -1))  &&
                (triggerValue > pTriggerSet[startRegionIndex].end))
            {
                pOutput0->startIndex    = startRegionIndex;
                pOutput0->endIndex      = startRegionIndex;
                regionIdentified        = TRUE;
                break;
            }  // If trigger is between two regions
            else if ((triggerValue > pTriggerSet[startRegionIndex].end)        &&
                     (triggerValue < pTriggerSet[startRegionIndex + 1].start))
            {
                interpolationRatio = IQSettingUtils::CalculateInterpolationRatio(
                                  static_cast<DOUBLE>(triggerValue),
                                  static_cast<DOUBLE>(pTriggerSet[startRegionIndex].end),
                                  static_cast<DOUBLE>(pTriggerSet[startRegionIndex + 1].start));

                pOutput0->startIndex    = startRegionIndex;
                pOutput0->endIndex      = startRegionIndex + 1;
                regionIdentified        = TRUE;
                break;
            } // If trigger is in one region
            else if (triggerValue <= pTriggerSet[startRegionIndex].end)
            {
                pOutput0->startIndex = startRegionIndex;
                pOutput0->endIndex   = pOutput0->startIndex;
                regionIdentified     = TRUE;
                break;
            }
        }

        if (FALSE == regionIdentified)
        {
            pOutput0->startIndex = 0;
            pOutput0->endIndex   = 0;
            /// @todo (CAMX-1812) Need to add logging for Common library
        }

        pOutput0->interpolationRatio = interpolationRatio;
    }

    return;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IQSettingUtils::ModuleInitialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL IQSettingUtils::ModuleInitialize(
    IQLibraryData* pData)
{
    BOOL result = TRUE;

    pData->pCustomticData = NULL;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IQSettingUtils::ModuleUninitialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL IQSettingUtils::ModuleUninitialize(
    IQLibraryData* pData)
{
    BOOL result = TRUE;

    pData->pCustomticData = NULL;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IQSettingUtils::DumpTriggerCondition
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID IQSettingUtils::DumpTriggerCondition(
    ISPIQTriggerData* pTriggerData)
{
    CAMX_LOG_INFO(CamxLogGroupISP, "AECexposureTime = %f",      pTriggerData->AECexposureTime);
    CAMX_LOG_INFO(CamxLogGroupISP, "AECexposureGainRatio = %f", pTriggerData->AECexposureGainRatio);
    CAMX_LOG_INFO(CamxLogGroupISP, "AECSensitivity = %f",       pTriggerData->AECSensitivity);
    CAMX_LOG_INFO(CamxLogGroupISP, "AECGain = %f",              pTriggerData->AECGain);
    CAMX_LOG_INFO(CamxLogGroupISP, "AECLuxIndex = %f",          pTriggerData->AECLuxIndex);
    CAMX_LOG_INFO(CamxLogGroupISP, "AWBleftGGainWB = %f",       pTriggerData->AWBleftGGainWB);
    CAMX_LOG_INFO(CamxLogGroupISP, "AWBleftBGainWB = %f",       pTriggerData->AWBleftBGainWB);
    CAMX_LOG_INFO(CamxLogGroupISP, "AWBleftRGainWB = %f",       pTriggerData->AWBleftRGainWB);
    CAMX_LOG_INFO(CamxLogGroupISP, "AWBColorTemperature = %f",  pTriggerData->AWBColorTemperature);
    CAMX_LOG_INFO(CamxLogGroupISP, "DRCGain = %f",              pTriggerData->DRCGain);
    CAMX_LOG_INFO(CamxLogGroupISP, "lensPosition = %f",         pTriggerData->lensPosition);
    CAMX_LOG_INFO(CamxLogGroupISP, "lensZoom = %f",             pTriggerData->lensZoom);
    CAMX_LOG_INFO(CamxLogGroupISP, "postScaleRatio = %f",       pTriggerData->postScaleRatio);
    CAMX_LOG_INFO(CamxLogGroupISP, "preScaleRatio = %f",        pTriggerData->preScaleRatio);
    CAMX_LOG_INFO(CamxLogGroupISP, "sensorImageWidth = %d",     pTriggerData->sensorImageWidth);
    CAMX_LOG_INFO(CamxLogGroupISP, "sensorImageHeight = %d",    pTriggerData->sensorImageHeight);
    CAMX_LOG_INFO(CamxLogGroupISP, "CAMIFWidth = %d",           pTriggerData->CAMIFWidth);
    CAMX_LOG_INFO(CamxLogGroupISP, "CAMIFHeight = %d",          pTriggerData->CAMIFHeight);
    CAMX_LOG_INFO(CamxLogGroupISP, "numberOfLED = %d",          pTriggerData->numberOfLED);
    CAMX_LOG_INFO(CamxLogGroupISP, "LEDSensitivity = %d",       pTriggerData->LEDSensitivity);
    CAMX_LOG_INFO(CamxLogGroupISP, "bayerPattern = %d",         pTriggerData->bayerPattern);
    CAMX_LOG_INFO(CamxLogGroupISP, "sensorOffsetX = %d",        pTriggerData->sensorOffsetX);
    CAMX_LOG_INFO(CamxLogGroupISP, "sensorOffsetY = %d",        pTriggerData->sensorOffsetY);
    CAMX_LOG_INFO(CamxLogGroupISP, "blackLevelOffset = %d",     pTriggerData->blackLevelOffset);
}
